#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
图像画布模块，提供图像显示和交互功能。
"""

import sys
import os
import cv2
import numpy as np
from PyQt5.QtCore import Qt, QPoint, pyqtSignal, QRect
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QColor, QCursor, QFont, QBrush
from PyQt5.QtWidgets import QWidget, QSizePolicy, QMenu, QDialog, QVBoxLayout, QHBoxLayout, QPushButton
from PIL import Image
import tempfile
import uuid

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

try:
    # 尝试直接导入（从项目根目录运行时）
    from src.core.image_processor import ImageProcessor
except ImportError:
    # 如果失败，尝试使用相对导入（从src目录运行时）
    from core.image_processor import ImageProcessor

# 移除调试输出
# print("ImageCanvas导入的模块:")
# print(f"QSizePolicy已导入: {'QSizePolicy' in globals()}")
# print(f"PyQt5.QtWidgets.QSizePolicy已导入: {'PyQt5.QtWidgets.QSizePolicy' in globals()}")

# 区域调整方向常量
RESIZE_NONE = 0
RESIZE_TOP_LEFT = 1
RESIZE_TOP = 2
RESIZE_TOP_RIGHT = 3
RESIZE_RIGHT = 4
RESIZE_BOTTOM_RIGHT = 5
RESIZE_BOTTOM = 6
RESIZE_BOTTOM_LEFT = 7
RESIZE_LEFT = 8
RESIZE_MOVE = 9

class ImageCanvas(QWidget):
    """图像显示和交互画布"""
    
    # 自定义信号
    region_selected = pyqtSignal(tuple)  # 区域选择信号 (x, y, w, h)
    region_dragged = pyqtSignal(tuple)   # 区域拖动信号 (x, y, w, h)
    point_clicked = pyqtSignal(int, int) # 点击位置信号 (x, y)
    region_right_clicked = pyqtSignal(int, QPoint)  # 右键点击区域信号 (区域索引, 全局位置)
    background_right_clicked = pyqtSignal(int, int, QPoint)  # 右键点击背景信号 (x, y, 全局位置)
    region_adjusted = pyqtSignal(int, tuple)  # 区域调整信号 (区域索引, (x, y, w, h))
    region_double_clicked = pyqtSignal(int)  # 区域双击信号 (区域索引)
    color_picked = pyqtSignal(list)  # 发送RGB颜色值
    
    def __init__(self, parent=None):
        """初始化图像画布
        
        参数:
            parent: 父级部件
        """
        super().__init__(parent)
        
        # 图像数据
        self.image = None  # 当前显示的图像
        self.image_data = None  # 原始图像数据（用于保存时使用）
        self.displayed_image = None  # 显示的像素图
        self.is_rgb = True  # 图像是否为RGB格式（True=RGB, False=BGR）
        
        # 区域数据
        self.regions = []  # 区域列表，每个元素为 (x, y, w, h, name?)
        self.current_index = -1  # 当前选中区域的索引
        self.adjusted_regions = []  # 调整后的区域坐标（用于放大显示）
        
        # 其他状态
        self.zoomed_region = None  # 放大区域：(x, y, w, h)
        self.original_image_copy = None  # 放大前原始图像的副本
        self.original_regions = None  # 放大前原始区域列表的副本
        self.is_zoomed = False  # 是否处于放大状态
        self.scale = 1.0  # 图像缩放比例
        self.scale_y = 1.0  # 图像Y轴缩放比例
        self.min_rect_size = 10  # 最小矩形大小
        self.is_adjusting = False  # 当前是否正在调整区域大小
        self.is_dragging = False  # 当前是否正在拖动区域
        self.drag_start_x = None  # 拖动开始的X坐标
        self.drag_start_y = None  # 拖动开始的Y坐标
        self.resize_direction = None  # 调整大小的方向
        self.orig_rect = None  # 调整大小前的原始矩形
        self.current_rect = None  # 当前正在绘制的矩形
        self.resize_handle_size = 10  # 调整柄大小
        
        # 界面设置
        self.suggested_area = None  # 建议的区域
        self.show_suggestion = True  # 是否显示建议区域
        self.pause_suggestion = False  # 是否暂停显示建议
        self.border_color = (255, 0, 0)  # 边框颜色
        self.sensitivity = 10  # 边框灵敏度（用于检测鼠标在边框上）
        self.color_picking_mode = False  # 颜色取样模式
        
        # 初始化界面
        self.init_ui()
    
    def init_ui(self):
        """初始化界面设置"""
        # 设置控件属性
        self.image = None  # 原始图像
        self.displayed_image = None  # 显示用的图像
        self.scale = 1.0  # 缩放比例
        self.scale_y = 1.0  # Y方向缩放比例（兼容性）
        self.offset_x = 0  # X偏移
        self.offset_y = 0  # Y偏移
        self.drag_start_x = None  # 拖拽起始X坐标
        self.drag_start_y = None  # 拖拽起始Y坐标
        self.is_dragging = False  # 是否正在拖拽
        self.drawing = False  # 是否正在绘制
        self.point1 = None  # 绘制矩形的起点
        self.current_rect = None  # 当前绘制的矩形
        self.regions = []  # 已添加区域列表
        self.current_index = -1  # 当前选中区域索引
        self.is_adjusting = False  # 是否正在调整区域
        self.adjust_direction = None  # 调整方向
        self.adjust_start_position = None  # 调整起始位置
        self.adjust_original_rect = None  # 调整前的矩形
        self.resize_handle_size = 10  # 调整柄大小
        self.is_zoomed = False  # 是否处于放大状态
        self.zoomed_region = None  # 放大的区域
        self.suggested_areas = []  # 建议选择区域列表
        self.show_suggestion = True  # 是否显示建议区域
        self.pause_suggestion = False  # 是否暂停建议区域功能
        self.color_picking_mode = False  # 颜色选择模式
        self.base_font_size = 10  # 字体基础大小
        self.sensitivity = 30  # 自动检测敏感度
        
        # 设置聚焦策略
        self.setFocusPolicy(Qt.StrongFocus)
        self.setMouseTracking(True)
        
        # 初始化界面
        self.init_ui()
    
    def init_ui(self):
        """初始化界面设置"""
        # 设置控件属性
        self.setMinimumSize(400, 300)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setMouseTracking(True)  # 跟踪鼠标移动
        self.setFocusPolicy(Qt.StrongFocus)  # 接收键盘事件
        self.setCursor(Qt.CrossCursor)  # 默认十字光标
    
    def set_image(self, image, is_rgb=True):
        """设置要显示的图像
        
        参数:
            image (numpy.ndarray): 图像数据
            is_rgb (bool): 图像是否为RGB格式，True表示RGB，False表示BGR
        """
        if image is None or image.size == 0:
            return
        
        print(f"设置图像: 形状={image.shape}, 类型={image.dtype}, 格式={'RGB' if is_rgb else 'BGR'}")
        if image.shape[0] > 0 and image.shape[1] > 0:
            print(f"输入图像左上角像素值: {image[0, 0]}")
        
        # 保存原始图像
        self.image = image.copy()
        self.image_data = self.image.copy()  # 保存原始图像数据
        self.is_rgb = is_rgb  # 保存图像格式信息
        
        print(f"保存图像后: self.image形状={self.image.shape}, self.image_data形状={self.image_data.shape}")
        if self.image.shape[0] > 0 and self.image.shape[1] > 0:
            print(f"self.image左上角像素值: {self.image[0, 0]}")
            print(f"self.image_data左上角像素值: {self.image_data[0, 0]}")
        
        # 更新显示
        self.update_display()
        
        # 清除状态
        self.regions = []
        self.current_index = -1
        self.current_rect = None
        self.is_dragging = False
        self.drag_start_x = None
        self.drag_start_y = None
        self.is_adjusting = False
        self.zoomed_region = None
    
    def update_display(self):
        """更新图像显示"""
        if self.image is None or self.image.size == 0:
            print("更新显示 - 图像为空")
            return
        
        print("开始更新图像显示")
        
        # 检查是否有放大区域
        if self.zoomed_region:
            # 提取放大区域
            x, y, w, h = self.zoomed_region
            print(f"处理放大区域: x={x}, y={y}, w={w}, h={h}")
            
            # 确保区域尺寸有效
            if w <= 0 or h <= 0:
                print("无效区域尺寸")
                return
                
            # 确保坐标在有效范围内
            img_h, img_w = self.image.shape[:2]
            x = max(0, min(x, img_w - 1))
            y = max(0, min(y, img_h - 1))
            w = min(w, img_w - x)
            h = min(h, img_h - y)
            
            # 再次检查尺寸
            if w <= 0 or h <= 0:
                print("调整后尺寸无效")
                return
                
            # 提取区域图像
            display_image = self.image[y:y+h, x:x+w].copy()
            print(f"提取放大区域图像: 形状={display_image.shape}")
            if display_image.shape[0] > 0 and display_image.shape[1] > 0:
                print(f"显示图像左上角像素值: {display_image[0, 0]}")
            
            # 直接使用regions中的坐标，因为在zoom_region中已经调整过了
            self.adjusted_regions = self.regions
            print(f"使用已调整的区域坐标: {len(self.adjusted_regions)}个区域")
        else:
            # 显示整个图像
            display_image = self.image.copy()
            print(f"使用整个图像: 形状={display_image.shape}")
            if display_image.shape[0] > 0 and display_image.shape[1] > 0:
                print(f"显示图像左上角像素值: {display_image[0, 0]}")
            
            self.adjusted_regions = self.regions.copy() if self.regions else []
            print(f"使用原始区域坐标: {len(self.adjusted_regions)}个区域")
            
        # 获取图像尺寸
        h, w = display_image.shape[:2]
        bytes_per_line = 3 * w
        
        # 确保数组内存连续
        if not display_image.flags['C_CONTIGUOUS']:
            print("显示图像数据不连续，进行连续化处理")
            display_image = np.ascontiguousarray(display_image)
        
        # 在Qt中，QImage.Format_RGB888需要RGB格式(而非BGR)
        # 所以如果图像是BGR格式，需要交换红蓝通道
        if not self.is_rgb:
            # 复制一份图像数据以避免修改原始数据
            rgb_image = display_image.copy()
            # 交换红蓝通道 BGR->RGB
            rgb_image[:, :, [0, 2]] = rgb_image[:, :, [2, 0]]
            print("已交换红蓝通道用于显示(BGR->RGB)")
            # 使用RGB格式创建QImage
            q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        else:
            # 如果已经是RGB格式，则直接使用
            print("图像已经是RGB格式，直接用于显示")
            q_image = QImage(display_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        
        print(f"创建用于显示的QImage: 尺寸=({w}x{h}), 格式=QImage.Format_RGB888")
        
        # 转换为QPixmap
        pixmap = QPixmap.fromImage(q_image)
        print(f"转换为QPixmap: 尺寸=({pixmap.width()}x{pixmap.height()})")
        
        # 检查pixmap是否有效
        if pixmap.isNull():
            print("QPixmap无效")
            return
        
        # 获取控件尺寸
        widget_w = self.width()
        widget_h = self.height()
        print(f"控件尺寸: {widget_w}x{widget_h}")
        
        # 确保控件尺寸有效
        if widget_w <= 0 or widget_h <= 0:
            print("控件尺寸无效")
            return
        
        # 调整大小以适应控件
        scaled_pixmap = pixmap.scaled(
            widget_w,
            widget_h,
            Qt.KeepAspectRatio,
            Qt.SmoothTransformation
        )
        print(f"缩放后QPixmap: 尺寸=({scaled_pixmap.width()}x{scaled_pixmap.height()})")
        
        # 检查缩放后的pixmap是否有效
        if scaled_pixmap.isNull():
            print("缩放后QPixmap无效")
            return
        
        # 计算缩放比例（添加安全检查）
        if w > 0 and h > 0:
            self.scale = scaled_pixmap.width() / float(w)
            self.scale_y = scaled_pixmap.height() / float(h)
            print(f"计算缩放比例: scale={self.scale}, scale_y={self.scale_y}")
        else:
            print("无法计算缩放比例，图像宽度或高度为0")
            return
        
        # 保存显示的像素图
        self.displayed_image = scaled_pixmap
        print("显示图像已更新")
        
        # 更新窗口
        self.update()
    
    def draw_regions(self, regions=None, current_index=None):
        """绘制区域
        
        参数:
            regions (list): 区域列表，每个区域为 (x, y, w, h, name)
            current_index (int): 当前选中的区域索引
        """
        if regions is not None:
            self.regions = regions
            
        if current_index is not None:
            self.current_index = current_index
            
        # 复制原图像进行绘制
        if self.image is not None:
            # 确保adjusted_regions是最新的
            if self.zoomed_region:
                # 放大状态下计算调整后的区域坐标
                self.update_adjusted_regions()
            else:
                # 直接使用self.regions
                self.adjusted_regions = self.regions.copy() if self.regions else []
                
            # 触发重绘
            self.update()
    
    def update_adjusted_regions(self):
        """更新调整后的区域坐标（用于放大显示）"""
        if not self.zoomed_region:
            self.adjusted_regions = self.regions.copy() if self.regions else []
            return
            
        # 提取放大区域
        x, y, w, h = self.zoomed_region
        
        # 调整区域绘制坐标
        self.adjusted_regions = []
        for region in self.regions:
            rx, ry, rw, rh = region[:4]
            
            # 计算相对于放大区域的坐标
            adj_x = rx - x
            adj_y = ry - y
            
            # 添加调整后的区域
            if len(region) > 4:
                self.adjusted_regions.append((adj_x, adj_y, rw, rh, region[4]))
            else:
                self.adjusted_regions.append((adj_x, adj_y, rw, rh))
    
    def paintEvent(self, event):
        """绘制事件处理"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        
        if self.displayed_image is not None:
            # 计算图像在窗口中的位置
            x_offset = (self.width() - self.displayed_image.width()) // 2
            y_offset = (self.height() - self.displayed_image.height()) // 2
            self.image_rect = QRect(x_offset, y_offset, self.displayed_image.width(), self.displayed_image.height())
            
            # 绘制图像
            painter.drawPixmap(self.image_rect, self.displayed_image)
            
            # 1. 绘制当前矩形（正在创建的区域）
            if self.current_rect is not None:
                pen = QPen(QColor(0, 255, 0), 2, Qt.DashLine)
                painter.setPen(pen)
                
                x, y, w, h = self.current_rect
                disp_x = int(x * self.scale) + self.image_rect.x()
                disp_y = int(y * self.scale) + self.image_rect.y()
                disp_w = int(w * self.scale)
                disp_h = int(h * self.scale)
                
                painter.drawRect(QRect(disp_x, disp_y, disp_w, disp_h))
            
            # 2. 绘制已添加的区域
            self.manual_draw_regions(painter)
            
            # 3. 绘制建议选择区域
            if hasattr(self, 'suggested_areas') and self.suggested_areas and self.show_suggestion and not self.pause_suggestion:
                # 设置虚线样式
                pen = QPen(QColor(255, 165, 0), 2, Qt.DashLine)  # 使用橙色
                pen.setDashPattern([5, 5])  # 设置虚线模式：5像素实线，5像素空白
                painter.setPen(pen)
                painter.setBrush(Qt.NoBrush)  # 不填充
                
                # 只绘制第一个建议区域
                rect = self.suggested_areas[0]
                x, y, w, h = rect
                
                # 转换为显示坐标
                disp_x = int(x * self.scale) + self.image_rect.x()
                disp_y = int(y * self.scale) + self.image_rect.y()
                disp_w = int(w * self.scale)
                disp_h = int(h * self.scale)
                
                # 绘制矩形
                painter.drawRect(QRect(disp_x, disp_y, disp_w, disp_h))
        
        painter.end()
    
    def resizeEvent(self, event):
        """Qt尺寸改变事件处理"""
        super().resizeEvent(event)
        
        # 更新图像显示以适应新尺寸
        self.update_display()
    
    def set_sensitivity(self, value):
        """设置像素差敏感度
        
        参数:
            value (int): 敏感度值
        """
        self.sensitivity = value
    
    def mousePressEvent(self, event):
        """鼠标按下事件处理"""
        if self.image is None or self.image.size == 0:
            return
            
        # 获取图像坐标
        img_x, img_y = self._get_image_coords(event.pos())
        
        # 检查坐标是否在图像范围内
        if img_x is None or img_y is None:
            return
            
        if event.button() == Qt.LeftButton:
            # 检查是否点击在现有区域上
            region_index = self._get_region_at_pos(event.pos())
            
            if region_index >= 0:
                # 更新当前选中区域索引
                self.current_index = region_index
                
                # 检查是否需要调整大小
                direction = self._get_resize_direction(event.pos(), region_index)
                
                if direction != RESIZE_NONE:
                    # 进入调整模式
                    self.is_adjusting = True
                    self.adjust_direction = direction
                    self.adjust_start_position = event.pos()
                    self.adjust_original_rect = self.regions[region_index]
                    
                    # 暂停建议区域显示
                    self.pause_suggestion = True
                    
                    # 刷新显示
                    self.update()
                    return
            else:
                # 如果点击在空白处，清除当前选中
                self.current_index = -1
                
                # 如果没有点击区域或调整点，开始拖动创建新区域
                self.is_dragging = True
                self.is_adjusting = False  # 确保不在调整状态
                # 保存图像坐标而非窗口坐标
                self.drag_start_x = img_x
                self.drag_start_y = img_y
                
                # 暂停建议区域显示
                self.pause_suggestion = True
                
                # 更新显示
                self.update()
        
        elif event.button() == Qt.RightButton:
            # 右键点击
            region_index = self._get_region_at_pos(event.pos())
            
            if region_index >= 0:
                # 点击在区域内，发送右键点击区域信号
                self.region_right_clicked.emit(region_index, event.globalPos())
            else:
                # 点击在背景上，发送右键点击背景信号
                self.background_right_clicked.emit(img_x, img_y, event.globalPos())
    
    def mouseDoubleClickEvent(self, event):
        """鼠标双击事件处理"""
        if self.image is None:
            return
            
        # 获取点击位置的区域索引
        region_index = self._get_region_at_pos(event.pos())
        
        if region_index >= 0:
            # 更新当前选中区域
            self.current_index = region_index
            
            # 如果已经在放大状态，则退出放大状态并发送双击信号
            if self.is_zoomed:
                self.region_double_clicked.emit(region_index)
                return
                
            # 创建调整对话框
            dialog = QDialog(self)
            dialog.setWindowTitle("区域精确调整")
            dialog.setModal(True)
            
            # 设置对话框大小
            dialog.resize(800, 600)
            
            # 创建垂直布局
            layout = QVBoxLayout(dialog)
            
            # 创建一个新的ImageCanvas用于显示放大的区域
            canvas = ImageCanvas(dialog)
            canvas.set_image(self.image.copy())
            # 只复制当前选中的区域
            selected_region = self.regions[region_index]
            canvas.regions = [selected_region]
            canvas.current_index = 0  # 因为只有一个区域，索引为0
            # 关闭建议区域功能
            canvas.show_suggestion = False
            
            # 放大选中的区域
            canvas.zoom_region(0)  # 因为只有一个区域，索引为0
            
            # 将画布添加到布局
            layout.addWidget(canvas)
            
            # 创建按钮布局
            button_layout = QHBoxLayout()
            
            # 创建确定和取消按钮
            ok_button = QPushButton("确定", dialog)
            cancel_button = QPushButton("取消", dialog)
            
            # 添加按钮到布局
            button_layout.addWidget(ok_button)
            button_layout.addWidget(cancel_button)
            
            # 将按钮布局添加到主布局
            layout.addLayout(button_layout)
            
            # 连接按钮信号
            def on_ok_clicked():
                # 获取调整后的区域坐标
                if canvas.regions:
                    adjusted_region = canvas.regions[0]  # 因为只有一个区域
                    # 恢复到原始坐标系
                    if canvas.zoomed_region:
                        crop_x, crop_y, _, _ = canvas.zoomed_region
                        rx, ry, rw, rh = adjusted_region[:4]
                        original_x = rx + crop_x
                        original_y = ry + crop_y
                        if len(adjusted_region) > 4:
                            self.regions[region_index] = (original_x, original_y, rw, rh, adjusted_region[4])
                        else:
                            self.regions[region_index] = (original_x, original_y, rw, rh)
                        
                        # 发送区域调整信号
                        self.region_adjusted.emit(region_index, self.regions[region_index])
                
                dialog.accept()
                
            def on_cancel_clicked():
                dialog.reject()
                
            ok_button.clicked.connect(on_ok_clicked)
            cancel_button.clicked.connect(on_cancel_clicked)
            
            # 显示对话框
            dialog.exec_()
            
            # 对话框关闭后重置当前选中区域
            self.current_index = -1
            self.update()
    
    def mouseMoveEvent(self, event):
        """鼠标移动事件处理"""
        if self.image is None:
            return
        
        # 获取图像坐标
        img_x, img_y = self._get_image_coords(event.pos())
        
        # 检查坐标是否在图像范围内
        if img_x is None or img_y is None:
            # 鼠标在图像外，清除建议区域
            if hasattr(self, 'suggested_areas'):
                self.suggested_areas = []
                self.update()
            return
        
        if self.is_dragging:
            # 拖动状态，更新矩形
            orig_x = self.drag_start_x
            orig_y = self.drag_start_y
            
            # 计算矩形
            x = min(orig_x, img_x)
            y = min(orig_y, img_y)
            w = abs(img_x - orig_x)
            h = abs(img_y - orig_y)
            
            self.current_rect = (x, y, w, h)
            
            # 更新显示
            self.update()
        elif self.is_adjusting and self.current_index >= 0:
            # 调整状态，根据方向调整区域大小
            self._adjust_region(event.pos())
        else:
            # 非拖动和调整状态，更新鼠标指针
            region_index = self._get_region_at_pos(event.pos())
            
            if region_index >= 0:
                # 鼠标在区域上，根据位置更新指针
                direction = self._get_resize_direction(event.pos(), region_index)
                self._update_cursor(direction)
                # 清除建议区域
                if hasattr(self, 'suggested_areas'):
                    self.suggested_areas = []
                    self.update()
            else:
                # 鼠标不在区域上，恢复默认指针
                self.setCursor(Qt.ArrowCursor)
                
                # 只在非拖动和非调整状态下检测建议区域
                if not self.is_dragging and not self.is_adjusting and self.show_suggestion and not self.pause_suggestion:
                    try:
                        # 尝试检测建议区域
                        processor = ImageProcessor()
                        # 使用当前鼠标位置作为检测中心点
                        suggested_rects = processor.detect_multiple_areas(self.image, img_x, img_y, self.sensitivity)
                        
                        # 更新建议区域列表
                        if suggested_rects:
                            self.suggested_areas = suggested_rects
                            self.current_mouse_pos = (img_x, img_y)  # 保存当前鼠标位置
                            self.update()  # 触发重绘
                        else:
                            # 如果没有检测到区域，清除现有的建议区域
                            if hasattr(self, 'suggested_areas'):
                                self.suggested_areas = []
                                self.update()
                                
                    except Exception as e:
                        if hasattr(self, 'suggested_areas'):
                            self.suggested_areas = []
                            self.update()
    
    def mouseReleaseEvent(self, event):
        """鼠标释放事件处理"""
        # 恢复建议区域显示
        self.pause_suggestion = False
        
        # 如果不是左键，则忽略
        if event.button() != Qt.LeftButton:
            return
            
        # 获取图像坐标
        img_x, img_y = self._get_image_coords(event.pos())
        
        # 检查坐标是否在图像范围内
        if img_x is None or img_y is None:
            self.is_dragging = False
            self.current_rect = None
            self.update()
            return
        
        # 检查是否有建议区域且不在拖动状态
        if not self.is_dragging and hasattr(self, 'suggested_areas') and self.suggested_areas:
            # 检查是否点击在建议区域内
            for i, rect in enumerate(self.suggested_areas):
                x, y, w, h = rect
                if x <= img_x <= x + w and y <= img_y <= y + h:
                    # 选择建议区域
                    self.current_rect = rect
                    self.region_selected.emit(rect)
                    # 清除建议区域
                    self.suggested_areas = []
                    self.update()
                    return
        
        # 检查是否在拖动
        if self.is_dragging:
            # 计算矩形
            w = abs(img_x - self.drag_start_x)
            h = abs(img_y - self.drag_start_y)
            x = min(img_x, self.drag_start_x)
            y = min(img_y, self.drag_start_y)
            
            # 如果矩形太小，则忽略
            if w < 5 or h < 5:
                self.is_dragging = False
                self.current_rect = None
                self.update()
                return
                
            # 创建矩形区域 - 转换为整数值
            self.current_rect = (int(x), int(y), int(w), int(h))
            
            # 触发区域选择信号
            self.region_selected.emit(self.current_rect)
            
            # 重置状态
            self.is_dragging = False
        # 检查是否在调整区域
        elif self.is_adjusting:
            # 如果当前正在调整区域，发送区域调整完成的信号
            if self.current_index >= 0 and len(self.regions) > self.current_index:
                region = self.regions[self.current_index]
                if len(region) >= 4:
                    # 发送区域已调整信号
                    self.region_adjusted.emit(self.current_index, region[:4])
                    
            # 重置调整状态
            self.is_adjusting = False
            self.adjust_direction = RESIZE_NONE
            self.adjust_start_position = None
            self.adjust_original_rect = None
            
            # 恢复默认鼠标样式
            self.setCursor(Qt.ArrowCursor)
            
        # 强制重绘
        self.update()
    
    def keyPressEvent(self, event):
        """键盘按下事件处理"""
        # 检查是否在精细调整模式
        if not self.is_adjusting or self.current_index < 0:
            # 不在精细调整模式，传递给基类
            super().keyPressEvent(event)
            return
            
        # 获取当前区域数据
        region = list(self.regions[self.current_index])
        changed = False
        
        # 使用方向键微调
        if event.key() == Qt.Key_Left:
            # 左箭头 - 水平缩小或左移
            if event.modifiers() & Qt.ShiftModifier:
                # 按住Shift - 左侧调整（缩小宽度）
                region[2] = max(10, region[2] - 1)
            elif event.modifiers() & Qt.ControlModifier:
                # 按住Ctrl - 右侧调整（缩小宽度）
                region[0] += 1
                region[2] = max(10, region[2] - 1)
            else:
                # 左移
                region[0] -= 1
            changed = True
            
        elif event.key() == Qt.Key_Right:
            # 右箭头 - 水平扩大或右移
            if event.modifiers() & Qt.ShiftModifier:
                # 按住Shift - 右侧调整（增加宽度）
                region[2] += 1
            elif event.modifiers() & Qt.ControlModifier:
                # 按住Ctrl - 左侧调整（增加宽度）
                region[0] -= 1
                region[2] += 1
            else:
                # 右移
                region[0] += 1
            changed = True
            
        elif event.key() == Qt.Key_Up:
            # 上箭头 - 垂直缩小或上移
            if event.modifiers() & Qt.ShiftModifier:
                # 按住Shift - 上侧调整（缩小高度）
                region[3] = max(10, region[3] - 1)
            elif event.modifiers() & Qt.ControlModifier:
                # 按住Ctrl - 下侧调整（缩小高度）
                region[1] += 1
                region[3] = max(10, region[3] - 1)
            else:
                # 上移
                region[1] -= 1
            changed = True
            
        elif event.key() == Qt.Key_Down:
            # 下箭头 - 垂直扩大或下移
            if event.modifiers() & Qt.ShiftModifier:
                # 按住Shift - 下侧调整（增加高度）
                region[3] += 1
            elif event.modifiers() & Qt.ControlModifier:
                # 按住Ctrl - 上侧调整（增加高度）
                region[1] -= 1
                region[3] += 1
            else:
                # 下移
                region[1] += 1
            changed = True
            
        elif event.key() == Qt.Key_Escape:
            # ESC键 - 退出精细调整模式
            self.is_adjusting = False
            
            # 如果处于放大状态，恢复原始视图
            if self.is_zoomed:
                self.restore_from_zoom()
            else:
                # 否则只更新绘制
                self.draw_regions()
            
            # 显示提示
            self.parent().statusBar().showMessage("已退出精细调整模式")
            return
        
        # 如果区域发生变化，更新并重绘
        if changed:
            # 检查区域是否超出图像边界
            if self.image is not None:
                h, w = self.image.shape[:2]
                region[0] = max(0, min(region[0], w - region[2]))
                region[1] = max(0, min(region[1], h - region[3]))
            
            # 更新区域
            self.regions[self.current_index] = tuple(region)
            
            # 重绘
            self.draw_regions()
            
            # 发送区域调整信号
            self.region_adjusted.emit(self.current_index, (region[0], region[1], region[2], region[3]))
        else:
            # 没有处理的键，传递给基类
            super().keyPressEvent(event)
    
    def _adjust_region(self, pos):
        """根据鼠标位置调整区域
        
        参数:
            pos (QPoint): 当前鼠标位置
        """
        if self.adjust_start_position is None or self.adjust_direction == RESIZE_NONE:
            return
            
        # 计算鼠标移动距离（窗口坐标）
        dx = pos.x() - self.adjust_start_position.x()
        dy = pos.y() - self.adjust_start_position.y()
        
        # 转换为图像坐标
        dx_img = int(dx / self.scale)
        dy_img = int(dy / self.scale)
        
        # 获取原始区域数据（只取前4个值）
        orig_x, orig_y, orig_w, orig_h = self.adjust_original_rect[:4]
        
        # 新的区域数据
        new_x, new_y, new_w, new_h = orig_x, orig_y, orig_w, orig_h
        
        # 根据调整方向更新区域
        if self.adjust_direction == RESIZE_MOVE:
            # 整体移动
            new_x = orig_x + dx_img
            new_y = orig_y + dy_img
        else:
            if self.adjust_direction in (RESIZE_LEFT, RESIZE_TOP_LEFT, RESIZE_BOTTOM_LEFT):
                # 左边调整
                new_x = orig_x + dx_img
                new_w = orig_w - dx_img
            
            if self.adjust_direction in (RESIZE_RIGHT, RESIZE_TOP_RIGHT, RESIZE_BOTTOM_RIGHT):
                # 右边调整
                new_w = orig_w + dx_img
            
            if self.adjust_direction in (RESIZE_TOP, RESIZE_TOP_LEFT, RESIZE_TOP_RIGHT):
                # 上边调整
                new_y = orig_y + dy_img
                new_h = orig_h - dy_img
            
            if self.adjust_direction in (RESIZE_BOTTOM, RESIZE_BOTTOM_LEFT, RESIZE_BOTTOM_RIGHT):
                # 下边调整
                new_h = orig_h + dy_img
        
        # 确保宽高不小于最小值
        MIN_SIZE = 10
        if new_w < MIN_SIZE:
            if self.adjust_direction in (RESIZE_LEFT, RESIZE_TOP_LEFT, RESIZE_BOTTOM_LEFT):
                new_x = orig_x + orig_w - MIN_SIZE
            new_w = MIN_SIZE
        
        if new_h < MIN_SIZE:
            if self.adjust_direction in (RESIZE_TOP, RESIZE_TOP_LEFT, RESIZE_TOP_RIGHT):
                new_y = orig_y + orig_h - MIN_SIZE
            new_h = MIN_SIZE
        
        # 确保不超出图像边界
        if self.image is not None:
            h, w = self.image.shape[:2]
            
            if new_x < 0:
                if self.adjust_direction == RESIZE_MOVE:
                    new_x = 0
                else:
                    new_w += new_x
                    new_x = 0
            
            if new_y < 0:
                if self.adjust_direction == RESIZE_MOVE:
                    new_y = 0
                else:
                    new_h += new_y
                    new_y = 0
            
            if new_x + new_w > w:
                if self.adjust_direction == RESIZE_MOVE:
                    new_x = w - new_w
                else:
                    new_w = w - new_x
            
            if new_y + new_h > h:
                if self.adjust_direction == RESIZE_MOVE:
                    new_y = h - new_h
                else:
                    new_h = h - new_y
        
        # 更新区域
        region = list(self.regions[self.current_index])
        region[0] = int(new_x)
        region[1] = int(new_y)
        region[2] = int(new_w)
        region[3] = int(new_h)
        self.regions[self.current_index] = tuple(region)
        
        # 更新显示
        self.update()
    
    def _update_cursor(self, direction):
        """根据调整方向更新鼠标指针
        
        参数:
            direction (int): 调整方向常量
        """
        if direction == RESIZE_TOP_LEFT or direction == RESIZE_BOTTOM_RIGHT:
            self.setCursor(Qt.SizeFDiagCursor)
        elif direction == RESIZE_TOP_RIGHT or direction == RESIZE_BOTTOM_LEFT:
            self.setCursor(Qt.SizeBDiagCursor)
        elif direction == RESIZE_LEFT or direction == RESIZE_RIGHT:
            self.setCursor(Qt.SizeHorCursor)
        elif direction == RESIZE_TOP or direction == RESIZE_BOTTOM:
            self.setCursor(Qt.SizeVerCursor)
        elif direction == RESIZE_MOVE:
            self.setCursor(Qt.SizeAllCursor)
        else:
            self.setCursor(Qt.ArrowCursor)
    
    def _get_image_coords(self, pos):
        """将窗口坐标转换为图像坐标
        
        参数:
            pos (QPoint): 窗口坐标
            
        返回:
            tuple: (图像x坐标, 图像y坐标) 或 (None, None)
        """
        if self.image is None or self.displayed_image is None:
            return None, None
        
        # 计算图像在窗口中的位置
        x_offset = (self.width() - self.displayed_image.width()) // 2
        y_offset = (self.height() - self.displayed_image.height()) // 2
        
        # 窗口坐标相对于图像的偏移
        rel_x = pos.x() - x_offset
        rel_y = pos.y() - y_offset
        
        # 检查是否在图像范围内
        if (0 <= rel_x < self.displayed_image.width() and
            0 <= rel_y < self.displayed_image.height()):
            # 转换为图像坐标
            img_x = int(rel_x / self.scale)
            img_y = int(rel_y / self.scale)
            
            # 检查边界
            img_x = max(0, min(img_x, self.image.shape[1] - 1))
            img_y = max(0, min(img_y, self.image.shape[0] - 1))
            
            return img_x, img_y
        else:
            return None, None
    
    def _get_region_at_pos(self, pos):
        """获取指定位置对应的区域索引
        
        参数:
            pos (QPoint): 窗口坐标
            
        返回:
            int: 区域索引（从0开始），如果没有区域则返回-1
        """
        if not self.regions:
            return -1
            
        # 获取图像坐标
        img_x, img_y = self._get_image_coords(pos)
        
        if img_x is None or img_y is None:
            return -1
            
        # 遍历区域检查位置（从后向前遍历，使得后创建的区域优先被选中）
        for i in range(len(self.regions) - 1, -1, -1):
            region = self.regions[i]
            # 确保区域数据有效
            if len(region) >= 4:
                x, y, w, h = region[:4]  # 只解包前4个值
                
                # 检查点是否在区域内
                if x <= img_x <= x + w and y <= img_y <= y + h:
                    return i  # 返回从0开始的索引
                    
        return -1
    
    def draw_suggested_region(self, painter):
        """绘制建议选择区域
        
        参数:
            painter (QPainter): 绘图对象
        """
        if not hasattr(self, 'suggested_areas') or not self.suggested_areas:
            return
            
        # 定义不同的颜色
        colors = [
            QColor(255, 165, 0),  # 橙色
            QColor(0, 255, 0),    # 绿色
            QColor(255, 0, 255),  # 紫色
            QColor(0, 255, 255),  # 青色
            QColor(255, 255, 0)   # 黄色
        ]
        
        for i, rect in enumerate(self.suggested_areas):
            x, y, w, h = rect
            
            # 转换为显示坐标
            disp_x = int(x * self.scale) + self.image_rect.x()
            disp_y = int(y * self.scale) + self.image_rect.y()
            disp_w = int(w * self.scale)
            disp_h = int(h * self.scale)
            
            # 选择颜色
            color = colors[i % len(colors)]
            
            # 设置虚线样式
            pen = QPen(color, 2, Qt.DashLine)
            pen.setDashPattern([5, 5])  # 设置虚线模式：5像素实线，5像素空白
            painter.setPen(pen)
            painter.setBrush(Qt.NoBrush)  # 不填充
            
            # 绘制矩形
            painter.drawRect(QRect(disp_x, disp_y, disp_w, disp_h))
            
            # 绘制提示文字
            font = painter.font()
            font.setBold(True)
            font.setPointSize(10)
            painter.setFont(font)
            painter.setPen(QPen(color, 2))  # 实线文字
            painter.drawText(QRect(disp_x + 5, disp_y - 20, 100, 20), Qt.AlignLeft, f"建议区域 {i+1}")
    
    def clear_suggested_region(self):
        """清除建议选择区域"""
        if hasattr(self, 'suggested_areas'):
            self.suggested_areas = []
        self.update()
    
    def _is_in_rect(self, x, y, rect):
        """检查坐标是否在矩形内
        
        参数:
            x (int): X坐标
            y (int): Y坐标
            rect (tuple): 矩形 (x, y, w, h)
            
        返回:
            bool: 在矩形内返回True，否则False
        """
        rx, ry, rw, rh = rect
        return rx <= x < rx + rw and ry <= y < ry + rh
    
    def _get_resize_direction(self, pos, region_index):
        """获取调整方向
        
        参数:
            pos (QPoint): 鼠标位置
            region_index (int): 区域索引
            
        返回:
            int: 调整方向
        """
        if self.regions is None or region_index < 0 or region_index >= len(self.regions):
            return RESIZE_NONE
            
        # 获取区域数据，确保有效
        region = self.regions[region_index]
        if len(region) < 4:
            return RESIZE_NONE
            
        # 解析区域坐标
        x, y, w, h = region[:4]
        
        # 计算显示坐标
        image_x = self.image_rect.x() if hasattr(self, 'image_rect') else 0
        image_y = self.image_rect.y() if hasattr(self, 'image_rect') else 0
        
        disp_x = int(x * self.scale) + image_x
        disp_y = int(y * self.scale) + image_y
        disp_w = int(w * self.scale)
        disp_h = int(h * self.scale)
        
        # 设置检测区域大小
        handle_size = self.resize_handle_size
        
        # 获取鼠标位置
        mx, my = pos.x(), pos.y()
        
        # 检查是否在调整手柄上
        # 左上角
        if abs(mx - disp_x) <= handle_size and abs(my - disp_y) <= handle_size:
            return RESIZE_TOP_LEFT
            
        # 右上角
        if abs(mx - (disp_x + disp_w)) <= handle_size and abs(my - disp_y) <= handle_size:
            return RESIZE_TOP_RIGHT
            
        # 左下角
        if abs(mx - disp_x) <= handle_size and abs(my - (disp_y + disp_h)) <= handle_size:
            return RESIZE_BOTTOM_LEFT
            
        # 右下角
        if abs(mx - (disp_x + disp_w)) <= handle_size and abs(my - (disp_y + disp_h)) <= handle_size:
            return RESIZE_BOTTOM_RIGHT
            
        # 上边中点
        if abs(mx - (disp_x + disp_w/2)) <= handle_size and abs(my - disp_y) <= handle_size:
            return RESIZE_TOP
            
        # 右边中点
        if abs(mx - (disp_x + disp_w)) <= handle_size and abs(my - (disp_y + disp_h/2)) <= handle_size:
            return RESIZE_RIGHT
            
        # 下边中点
        if abs(mx - (disp_x + disp_w/2)) <= handle_size and abs(my - (disp_y + disp_h)) <= handle_size:
            return RESIZE_BOTTOM
            
        # 左边中点
        if abs(mx - disp_x) <= handle_size and abs(my - (disp_y + disp_h/2)) <= handle_size:
            return RESIZE_LEFT
            
        # 检查是否在区域内部（用于移动）
        if (disp_x + handle_size < mx < disp_x + disp_w - handle_size and 
            disp_y + handle_size < my < disp_y + disp_h - handle_size):
            return RESIZE_MOVE
            
        return RESIZE_NONE
    
    def zoom_region(self, region_index):
        """放大指定区域以便精调
        
        参数:
            region_index (int): 要放大的区域索引
        返回:
            bool: 是否成功放大
        """
        if region_index < 0 or region_index >= len(self.regions) or self.image is None:
            return False
            
        # 已经是放大状态，不再执行
        if self.is_zoomed:
            return True
            
        # 获取区域数据
        region = self.regions[region_index]
        if len(region) < 4:
            return False
            
        x, y, w, h = region[:4]
        
        # 检查区域尺寸是否有效
        if w <= 0 or h <= 0:
            return False
            
        # 获取图像尺寸
        img_h, img_w = self.image.shape[:2]
        
        # 确保区域在图像范围内
        x = max(0, min(x, img_w - 1))
        y = max(0, min(y, img_h - 1))
        w = min(w, img_w - x)
        h = min(h, img_h - y)
        
        # 再次检查区域尺寸
        if w <= 0 or h <= 0:
            return False
            
        # 使用固定的边距
        margin = 10
        
        # 计算扩展后的区域，确保不超出图像边界
        crop_x = max(0, x - margin)
        crop_y = max(0, y - margin)
        crop_w = min(img_w - crop_x, w + 2 * margin)
        crop_h = min(img_h - crop_y, h + 2 * margin)
        
        # 确保裁剪区域有效
        if crop_w <= 0 or crop_h <= 0:
            return False
            
        # 备份原始图像和区域列表
        self.original_image_copy = self.image.copy()
        self.original_regions = self.regions.copy()
        
        # 保存放大区域的原始坐标
        self.zoomed_region = (crop_x, crop_y, crop_w, crop_h)
        
        try:
            # 裁剪区域
            cropped = self.image[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w].copy()
            
            # 检查裁剪结果
            if cropped is None or cropped.size == 0:
                return False
                
            # 调整区域坐标（相对于裁剪区域）
            adjusted_regions = []
            for i, region in enumerate(self.regions):
                if len(region) >= 4:
                    rx, ry, rw, rh = region[:4]
                    
                    # 计算相对坐标
                    new_x = rx - crop_x
                    new_y = ry - crop_y
                    
                    # 确保新坐标不为负
                    new_x = max(0, new_x)
                    new_y = max(0, new_y)
                    
                    # 确保不超出裁剪区域
                    new_w = min(rw, crop_w - new_x)
                    new_h = min(rh, crop_h - new_y)
                    
                    # 只添加与裁剪区域有交集的区域
                    if new_w > 0 and new_h > 0:
                        if len(region) > 4:
                            adjusted_regions.append((new_x, new_y, new_w, new_h, region[4]))
                        else:
                            adjusted_regions.append((new_x, new_y, new_w, new_h))
            
            # 更新区域列表
            self.regions = adjusted_regions
            
            # 设置新的图像
            self.image = cropped
            self.is_zoomed = True
            
            # 更新显示
            self.update_display()
            
            return True
            
        except Exception as e:
            # 恢复原始状态
            self.image = self.original_image_copy
            self.regions = self.original_regions
            self.original_image_copy = None
            self.original_regions = None
            self.zoomed_region = None
            self.is_zoomed = False
            return False
    
    def restore_from_zoom(self):
        """从放大状态恢复到原始视图
        
        返回:
            bool: 是否成功恢复
        """
        if not self.is_zoomed or self.original_image_copy is None or self.zoomed_region is None:
            return False
            
        try:
            # 获取放大区域的原始坐标
            crop_x, crop_y, crop_w, crop_h = self.zoomed_region
            
            # 如果当前选中区域有调整，更新其在原始区域列表中的坐标
            if self.current_index >= 0 and self.current_index < len(self.regions):
                current_region = self.regions[self.current_index]
                if len(current_region) >= 4:
                    rx, ry, rw, rh = current_region[:4]
                    
                    # 计算在原始图像中的绝对坐标
                    abs_x = rx + crop_x
                    abs_y = ry + crop_y
                    
                    # 确保坐标在图像范围内
                    abs_x = max(0, min(abs_x, self.original_image_copy.shape[1] - rw))
                    abs_y = max(0, min(abs_y, self.original_image_copy.shape[0] - rh))
                    
                    # 更新原始区域列表中的坐标
                    if len(current_region) > 4:
                        self.original_regions[self.current_index] = (abs_x, abs_y, rw, rh, current_region[4])
                    else:
                        self.original_regions[self.current_index] = (abs_x, abs_y, rw, rh)
            
            # 恢复原始图像和区域列表
            self.image = self.original_image_copy
            self.regions = self.original_regions
            
            # 清理状态
            self.original_image_copy = None
            self.original_regions = None
            self.zoomed_region = None
            self.is_zoomed = False
            
            # 更新显示
            self.update_display()
            
            return True
            
        except Exception as e:
            return False
    
    def enable_color_picking(self, enabled):
        """启用/禁用颜色选择模式
        
        参数:
            enabled (bool): 是否启用颜色选择
        """
        self.color_picking_mode = enabled
        
        # 更新鼠标样式
        if enabled:
            self.setCursor(Qt.CrossCursor)
        else:
            self.setCursor(Qt.ArrowCursor)
    
    def get_color_at(self, x, y):
        """获取指定位置的颜色
        
        参数:
            x (int): X坐标
            y (int): Y坐标
            
        返回:
            list: RGB颜色值 [r, g, b] 或 None
        """
        try:
            # 将屏幕坐标转换为图像坐标
            if hasattr(self, 'image_rect') and self.image_rect.contains(x, y):
                # 调整为图像相对坐标
                img_x = int((x - self.image_rect.x()) / self.scale_x)
                img_y = int((y - self.image_rect.y()) / self.scale_y)
                
                # 确保在图像范围内
                if isinstance(self.image, np.ndarray):
                    h, w = self.image.shape[:2]
                    if 0 <= img_x < w and 0 <= img_y < h:
                        # 获取BGR颜色值
                        color = self.image[img_y, img_x]
                        # 转换为RGB
                        return [int(color[2]), int(color[1]), int(color[0])]
                else:
                    # QImage
                    qimg = self.image
                    if 0 <= img_x < qimg.width() and 0 <= img_y < qimg.height():
                        pixel = qimg.pixel(img_x, img_y)
                        qcolor = QColor(pixel)
                        return [qcolor.red(), qcolor.green(), qcolor.blue()]
        except Exception as e:
            return None
    
    def zoom_to_region(self, region_index):
        """放大显示指定区域
        
        参数:
            region_index (int): 区域索引
        """
        # 检查索引有效性和图像是否已加载
        if self.image is None or region_index < 0 or region_index >= len(self.regions):
            return False
            
        # 获取区域数据
        region = self.regions[region_index]
        x, y, w, h = region[:4]
        
        # 确保区域有效
        if w <= 0 or h <= 0:
            return False
            
        # 计算放大区域（添加边距）
        margin = 20  # 像素边距
        zoom_x = max(0, x - margin)
        zoom_y = max(0, y - margin)
        zoom_w = min(w + 2 * margin, self.image.shape[1] - zoom_x)
        zoom_h = min(h + 2 * margin, self.image.shape[0] - zoom_y)
        
        # 保存放大区域信息
        self.zoomed_region = (zoom_x, zoom_y, zoom_w, zoom_h)
        
        # 更新显示
        self.update_display()
        
        return True
    
    def reset_zoom(self):
        """重置缩放状态"""
        if self.zoomed_region:
            self.zoomed_region = None
            self.update_display()
    
    def clear_regions(self):
        """清除所有区域"""
        self.regions = []
        self.current_index = -1
        self.zoomed_region = None
        self.current_rect = None
        self.is_adjusting = False
        self.is_dragging = False
        self.drag_start_x = None
        self.drag_start_y = None
        self.resize_direction = None
        self.suggested_areas = []
        self.show_suggestion = True
        self.pause_suggestion = False
        self.color_picking_mode = False
        
        # 更新显示
        self.update()
    
    def suggest_area(self, rect=None):
        """建议剪切区域
        
        参数:
            rect: 手动指定的区域，如果为None则自动计算
        """
        if self.image is None or self.pause_suggestion:
            return
            
        if rect:
            self.suggested_areas = [rect]
        else:
            # 使用OpenCV自动检测区域
            try:
                img_np = self.convert_qimage_to_numpy(self.image)
                gray = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
                blurred = cv2.GaussianBlur(gray, (5, 5), 0)
                edged = cv2.Canny(blurred, 50, 100)
                
                # 查找轮廓
                contours, _ = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
                
                # 筛选有效的轮廓
                valid_rects = []
                min_area = 400  # 最小面积阈值
                for contour in contours:
                    area = cv2.contourArea(contour)
                    if area > min_area:
                        x, y, w, h = cv2.boundingRect(contour)
                        # 确保区域合理
                        if w > 20 and h > 20:
                            # 如果区域接近图像边缘，则跳过
                            if not (x < 10 and y < 10 and 
                                x + w > self.image.shape[1] - 10 and 
                                y + h > self.image.shape[0] - 10):
                                valid_rects.append((x, y, w, h))
                
                # 按面积排序
                valid_rects.sort(key=lambda r: r[2] * r[3], reverse=True)
                
                # 取前5个最大的区域
                self.suggested_areas = valid_rects[:5]
                
            except Exception as e:
                self.suggested_areas = []
                
        # 更新显示
        self.update()
    
    def update_region(self, region_index, rect):
        """更新指定区域的坐标和大小
        
        参数:
            region_index (int): 区域索引
            rect (tuple): 区域坐标和大小 (x, y, w, h) 或 (x, y, w, h, name)
        """
        if region_index < 0 or region_index >= len(self.regions):
            return
            
        # 获取区域名称
        name = None
        if len(self.regions[region_index]) > 4:
            name = self.regions[region_index][4]
        
        # 检查rect是否包含名称
        if len(rect) > 4:
            # 使用传入的名称
            name = rect[4]
            
        # 更新区域
        if name:
            self.regions[region_index] = (rect[0], rect[1], rect[2], rect[3], name)
        else:
            self.regions[region_index] = rect
            
        # 如果当前放大显示该区域，则更新显示
        if self.zoomed_region is not None and self.current_index == region_index:
            self.zoom_to_region(region_index)
            
        # 更新显示
        self.update()
    
    def draw_manual_rect(self, x, y, w, h, color, thickness=2, style=Qt.SolidLine):
        """手动绘制指定区域的矩形框
        
        参数:
            x, y, w, h: 区域坐标和尺寸
            color: 颜色对象
            thickness: 线条粗细
            style: 线条样式
        """
        # 检查图像是否已加载
        if self.image is None or self.displayed_image is None:
            return
            
        # 创建QPainter
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        
        # 设置画笔
        pen = QPen(color, thickness, style)
        painter.setPen(pen)
        painter.setBrush(Qt.NoBrush)
        
        # 计算显示坐标
        disp_x = int(x * self.scale_x) + self.image_rect.x()
        disp_y = int(y * self.scale_y) + self.image_rect.y()
        disp_w = int(w * self.scale_x)
        disp_h = int(h * self.scale_y)
        
        # 绘制矩形
        painter.drawRect(disp_x, disp_y, disp_w, disp_h)
        
        # 结束绘制
        painter.end()
        
    def manual_draw_regions(self, painter=None):
        """手动绘制区域，用于处理问题
        
        参数:
            painter: QPainter对象，如果为None则使用update()更新画布
        """
        # 检查区域和图像
        if not self.regions or self.image is None:
            if painter is None:
                self.update()  # 没有传入painter，使用update强制更新画布
            return
            
        # 如果没有传入painter，则使用update()强制更新画布
        if painter is None:
            self.update()
            return
            
        # 获取图像尺寸
        img_h, img_w = self.image.shape[:2]
        
        # 遍历区域（从前向后遍历，保持与显示顺序一致）
        for i, region in enumerate(self.regions):
            if not region or len(region) < 4:
                continue
                
            # 解析区域坐标
            x, y, w, h = region[:4]
            
            # 确保坐标在图像范围内
            x = max(0, min(x, img_w - w))
            y = max(0, min(y, img_h - h))
            
            # 将相对坐标转换为显示坐标
            image_x = self.image_rect.x() if hasattr(self, 'image_rect') else 0
            image_y = self.image_rect.y() if hasattr(self, 'image_rect') else 0
            
            # 计算显示坐标时考虑图像的实际缩放比例
            disp_x = int(x * self.scale) + image_x
            disp_y = int(y * self.scale) + image_y
            disp_w = int(w * self.scale)
            disp_h = int(h * self.scale)
            
            # 选择颜色
            # 使用严格的相等比较，确保只有当前选中的区域显示为红色
            is_selected = (i == self.current_index)
            color = QColor(255, 0, 0) if is_selected else QColor(0, 0, 255)
            
            # 设置画笔
            pen = QPen(color, 2, Qt.SolidLine)
            painter.setPen(pen)
            
            # 绘制矩形
            painter.drawRect(QRect(disp_x, disp_y, disp_w, disp_h))
            
            # 显示区域索引（从1开始）
            font = painter.font()
            font.setBold(True)
            font.setPointSize(10)
            painter.setFont(font)
            
            # 根据选中状态设置不同的文字颜色
            text_color = QColor(255, 255, 0) if is_selected else QColor(255, 255, 255)
            painter.setPen(text_color)
            
            # 在区域左上角显示索引（从1开始）
            index_text = str(i + 1)
            painter.drawText(QRect(disp_x + 2, disp_y + 2, 20, 20), Qt.AlignLeft | Qt.AlignTop, index_text)
            
            # 只为当前选中的区域绘制调整手柄
            if is_selected:
                # 设置手柄样式
                handle_size = self.resize_handle_size
                painter.setPen(QPen(QColor(255, 255, 0), 2, Qt.SolidLine))  # 黄色边框
                painter.setBrush(QBrush(QColor(255, 255, 0, 180)))  # 半透明黄色填充
                
                # 绘制四角手柄
                # 左上角
                painter.drawRect(QRect(disp_x - handle_size//2, disp_y - handle_size//2, handle_size, handle_size))
                # 右上角
                painter.drawRect(QRect(disp_x + disp_w - handle_size//2, disp_y - handle_size//2, handle_size, handle_size))
                # 左下角
                painter.drawRect(QRect(disp_x - handle_size//2, disp_y + disp_h - handle_size//2, handle_size, handle_size))
                # 右下角
                painter.drawRect(QRect(disp_x + disp_w - handle_size//2, disp_y + disp_h - handle_size//2, handle_size, handle_size))
                
                # 绘制中点手柄
                # 上中点
                painter.drawRect(QRect(disp_x + disp_w//2 - handle_size//2, disp_y - handle_size//2, handle_size, handle_size))
                # 右中点
                painter.drawRect(QRect(disp_x + disp_w - handle_size//2, disp_y + disp_h//2 - handle_size//2, handle_size, handle_size))
                # 下中点
                painter.drawRect(QRect(disp_x + disp_w//2 - handle_size//2, disp_y + disp_h - handle_size//2, handle_size, handle_size))
                # 左中点
                painter.drawRect(QRect(disp_x - handle_size//2, disp_y + disp_h//2 - handle_size//2, handle_size, handle_size))
    
    def contextMenuEvent(self, event):
        """右键菜单事件处理"""
        if self.image is None:
            return
            
        # 获取点击位置的区域索引
        region_index = self._get_region_at_pos(event.pos())
        
        # 创建菜单
        menu = QMenu(self)
        
        if region_index >= 0:
            # 点击在区域上
            self.current_index = region_index
            
            if self.is_zoomed:
                # 已经处于放大状态，显示恢复选项
                restore_action = menu.addAction("恢复原始视图")
                restore_action.triggered.connect(self.restore_from_zoom)
            else:
                # 未放大，显示放大选项
                zoom_action = menu.addAction("放大区域进行精确调整")
                zoom_action.triggered.connect(lambda: self._handle_zoom_action(region_index))
        
        # 显示菜单
        menu.exec_(event.globalPos())
    
    def _handle_zoom_action(self, region_index):
        """处理放大操作
        
        参数:
            region_index (int): 区域索引
        """
        if self.zoom_region(region_index):
            # 进入调整模式
            self.is_adjusting = True
            
            # 显示提示
            if hasattr(self, 'parent') and self.parent() and hasattr(self.parent(), 'statusBar'):
                self.parent().statusBar().showMessage("进入精细调整模式 - 使用方向键微调区域大小，ESC退出")
            
            # 更新显示
            self.update() 
    
    def save_region_image(self, region_index, save_path):
        """保存区域图像
        
        参数:
            region_index (int): 区域索引
            save_path (str): 保存路径
        
        返回:
            bool: 是否成功保存
        """
        if region_index < 0 or region_index >= len(self.regions):
            print(f"无效的区域索引: {region_index}")
            return False
            
        # 获取区域坐标
        region = self.regions[region_index]
        if not region or len(region) < 4:
            print("无效的区域数据")
            return False
            
        x, y, w, h = region[:4]
        
        # 确保坐标在有效范围内
        img_h, img_w = self.image_data.shape[:2]
        x = max(0, min(x, img_w - 1))
        y = max(0, min(y, img_h - 1))
        w = min(w, img_w - x)
        h = min(h, img_h - y)
        
        # 检查区域尺寸是否有效
        if w <= 0 or h <= 0:
            print("无效的区域尺寸")
            return False
        
        print(f"区域坐标: x={x}, y={y}, w={w}, h={h}")
        print(f"原始图像形状: {self.image_data.shape}, 类型: {self.image_data.dtype}")
        print(f"原始图像左上角像素值: {self.image_data[y, x]}")
        
        # 裁剪区域
        cropped = self.image_data[y:y+h, x:x+w].copy()
        print(f"裁剪后图像形状: {cropped.shape}, 类型: {cropped.dtype}")
        print(f"裁剪后图像左上角像素值: {cropped[0, 0]}")
        
        # 直接使用PIL保存图像，避免颜色转换
        try:
            print("直接使用PIL保存图像，避免颜色空间转换")
            
            # 创建PIL图像对象直接保存
            print(f"创建PIL图像, 模式: RGB")
            img = Image.fromarray(cropped.astype('uint8'), 'RGB')
            print(f"PIL图像尺寸: {img.size}, 模式: {img.mode}")
            
            # 获取文件扩展名以确定格式
            _, ext = os.path.splitext(save_path)
            format_type = "PNG"  # 默认格式
            if ext.lower() in ['.jpg', '.jpeg']:
                format_type = "JPEG"
            elif ext.lower() == '.bmp':
                format_type = "BMP"
                
            # 直接保存
            img.save(save_path, format_type)
            print(f"已保存图像: {save_path}")
            
            # 验证保存的图像
            saved_img = np.array(Image.open(save_path))
            print(f"验证保存的图像: 形状={saved_img.shape}, 类型={saved_img.dtype}")
            print(f"保存图像左上角像素值: {saved_img[0, 0]}")
            print(f"裁剪区域与保存图像像素值是否一致: {np.array_equal(cropped[0, 0], saved_img[0, 0])}")
            
            return True
        except Exception as e:
            print(f"直接保存失败: {e}，尝试使用临时文件中转")
            
            # 使用临时文件中转的方式来修正颜色问题
            try:
                import tempfile
                import uuid
                
                # 创建临时文件路径
                temp_dir = tempfile.gettempdir()
                temp_filename = f"temp_img_{uuid.uuid4().hex}.png"
                temp_path = os.path.join(temp_dir, temp_filename)
                
                print(f"使用临时文件中转: {temp_path}")
                
                # 1. 保存为临时文件
                img = Image.fromarray(cropped.astype('uint8'), 'RGB')
                img.save(temp_path)
                print(f"已保存临时文件: {temp_path}")
                
                # 2. 直接读取临时文件并保存为最终文件
                temp_img = Image.open(temp_path)
                temp_img.save(save_path)
                print(f"已保存最终图像: {save_path}")
                
                # 3. 删除临时文件
                try:
                    os.remove(temp_path)
                    print(f"已删除临时文件: {temp_path}")
                except Exception as e:
                    print(f"无法删除临时文件: {e}")
                
                # 验证最终保存的图像
                final_img = np.array(Image.open(save_path))
                print(f"最终图像形状: {final_img.shape}, 类型: {final_img.dtype}")
                print(f"最终图像左上角像素值: {final_img[0, 0]}")
                print(f"裁剪区域与最终图像像素值是否一致: {np.array_equal(cropped[0, 0], final_img[0, 0])}")
                
                return True
            except Exception as e:
                print(f"保存图像时出错: {e}")
                return False
    
    def save_all_regions(self, save_dir):
        """保存所有区域的图像
        
        参数:
            save_dir (str): 保存目录
        """
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
            
        saved_count = 0
        for i, region in enumerate(self.regions):
            if region and len(region) >= 4:
                save_path = os.path.join(save_dir, f'region_{i+1}.jpg')
                if self.save_region_image(i, save_path):
                    saved_count += 1
                    
        return saved_count 